class_name Player
extends CharacterBody2D

@onready var body_collision: CollisionShape2D = %BodyCollision
@onready var body_bugs: GPUParticles2D = %BodyBugs
@onready var skill_bugs: GPUParticles2D = %SkillBugs
@onready var attack_timer: Timer = %AttackTimer
@onready var touch_attack_area: Area2D = %TouchAttackArea
@onready var touch_attack_collision: CollisionShape2D = %TouchAttackCollision
@onready var attack_area: Area2D = %AttackArea
@onready var attack_collision: CollisionShape2D = %AttackCollision
@onready var body_image: Node2D = %Body
@onready var animation_player: AnimationPlayer = $AnimationPlayer

@export var health := 30
@export var body_range := 30.0
@export var touch_attack_range := 60.0
@export var attack_range := 60.0
@export var attack_delay := 1.0
@export var attack_damage := 1.0

var cur_health := health
var cur_body_range := body_range
var cur_touch_attack_range := touch_attack_range
var cur_attack_range := attack_range

var body_process_material: ParticleProcessMaterial
var skill_process_material: ParticleProcessMaterial

var target_rot := 0.0

signal on_attack(range: float, damage: float)

func _ready() -> void:
	PlayManager.player = self
	body_process_material = body_bugs.process_material as ParticleProcessMaterial
	skill_process_material = skill_bugs.process_material as ParticleProcessMaterial
	attack_timer.timeout.connect(_on_attack_timer_timeout)
	attack_timer.start(attack_delay)
	
	attack_area.body_entered.connect(_on_attack_area_body_entered)
	reset()

func reset() -> void:
	attack_timer.stop()
	cur_health = health
	calc_range()
	update_collision_range()
	update_body_paricles()
	attack_timer.start(attack_delay)
	animation_player.play("move")

func _physics_process(_delta: float) -> void:
	velocity = Vector2.ZERO
	if Input.is_physical_key_pressed(KEY_D) || Input.is_physical_key_pressed(KEY_RIGHT):
		velocity.x += 1
	if Input.is_physical_key_pressed(KEY_A) || Input.is_physical_key_pressed(KEY_LEFT):
		velocity.x -= 1
	if Input.is_physical_key_pressed(KEY_W) || Input.is_physical_key_pressed(KEY_UP):
		velocity.y -= 1
	if Input.is_physical_key_pressed(KEY_S) || Input.is_physical_key_pressed(KEY_DOWN):
		velocity.y += 1

	# Normalize the velocity vector to ensure consistent speed in all directions
	if velocity.length() > 0:
		target_rot = velocity.angle()
		velocity = velocity.normalized() * 100
	move_and_slide()

	if abs(target_rot - body_image.rotation) > 0.01:
		body_image.rotation = lerp_angle(body_image.rotation, target_rot, 0.1)
	else:
		body_image.rotation = target_rot

func calc_range() -> void:
	cur_body_range = body_range * float(cur_health) / float(health)
	cur_touch_attack_range = touch_attack_range * float(cur_health) / float(health)
	cur_attack_range = attack_range * float(cur_health) / float(health)

func update_collision_range() -> void:
	(body_collision.shape as CircleShape2D).radius = cur_body_range
	(touch_attack_collision.shape as CircleShape2D).radius = cur_touch_attack_range

func update_body_paricles() -> void:
	body_bugs.amount = (cur_health if cur_health > 10 else 10) * 2
	body_process_material.emission_ring_radius = cur_body_range

	skill_bugs.amount = (cur_health if cur_health > 10 else 10) * 3

func play_attack(time: float, radius: float) -> void:
	skill_bugs.rotation = radius
	attack_area.rotation = radius
	skill_bugs.emitting = true
	var tween := create_tween()
	var half_time = time * 0.5
	tween.set_process_mode(Tween.TWEEN_PROCESS_PHYSICS)
	tween.tween_property(skill_process_material, "emission_shape_offset", Vector3(cur_attack_range, 0, 0), half_time)
	tween.parallel().tween_property(skill_process_material, "emission_box_extents", Vector3(cur_attack_range * 0.1, 30, 10), half_time)
	tween.parallel().tween_property(skill_process_material, "radial_velocity_min", 100, half_time)
	tween.parallel().tween_property(skill_process_material, "radial_velocity_max", 100, half_time)
	tween.parallel().tween_property(attack_collision, "position", Vector2(cur_attack_range, 0), half_time)
	
	tween.tween_property(skill_process_material, "emission_shape_offset", Vector3.ZERO, half_time)
	tween.parallel().tween_property(skill_process_material, "emission_box_extents", Vector3.ONE, half_time)
	tween.parallel().tween_property(skill_process_material, "radial_velocity_min", -100, half_time)
	tween.parallel().tween_property(skill_process_material, "radial_velocity_max", -100, half_time)
	tween.parallel().tween_property(attack_collision, "position", Vector2.ZERO, half_time)

	
	tween.tween_callback(func() -> void:
		skill_bugs.emitting = false
	)

func attack() -> void:
	var bodies = touch_attack_area.get_overlapping_bodies()
	var nearest_body: CharacterBody2D = null
	var distance := INF
	for body in bodies:
		if body is Enemy_Impl:
			var dist: float = (body.global_position - global_position).length()
			if dist < distance:
				distance = dist
				nearest_body = body

	if nearest_body != null:
		# get nearest body direction
		var body_dir := (nearest_body.global_position - global_position).normalized()
		# get angle
		var radius := body_dir.angle()
		play_attack(0.5, radius)
		on_attack.emit(cur_attack_range, attack_damage)
		return

func _on_attack_timer_timeout() -> void:
	attack()

func _on_attack_area_body_entered(body: Node2D) -> void:
	if body is Enemy_Impl:
		body.hurt(global_position - body.global_position)
